/*
	Copyright (C) 2010 2011 Rémi Thebault

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <QDebug>
#include <procycle/cycle.h>
#include <procycle/atomtask.h>

#include "tasktreemodel.h"
#include "procycle.h"


TaskTreeModel::TaskTreeModel(PC::Cycle* cycle, ProCycle *parent)
:	QAbstractItemModel(parent)
,	PCChild(parent)
{
	m_root = new TaskTreeItem(cycle);
}

TaskTreeModel::~TaskTreeModel() {
	delete m_root;
}

void TaskTreeModel::resetCycle(PC::Cycle *cycle) {
	delete m_root;
	m_root = new TaskTreeItem(cycle);
	reset();
}


QVariant
TaskTreeModel::data(const QModelIndex &index, int role) const {
	if (!index.isValid()) {
		return QVariant();
	}
	if (role ==  Qt::DisplayRole || role == Qt::EditRole) {
		TaskTreeItem* item = static_cast<TaskTreeItem*>(index.internalPointer());
		PC::Task::Data td = column(index.column());
		return item->data(td);
	}
	return QVariant();
}


Qt::ItemFlags
TaskTreeModel::flags(const QModelIndex &index) const {
	if (!index.isValid()) {
		return 0;
	}
	switch (column(index.column())) {
	case PC::Task::DATA_NAME:
		return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
	case PC::Task::DATA_LENGTH: {
			TaskTreeItem* item = static_cast<TaskTreeItem*>(index.internalPointer());
			PC::AtomTask* at = dynamic_cast<PC::AtomTask*>(item->task());
			if (at) {
				return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
			}
		}
		break;
	default:
		break;
	}
	return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

bool TaskTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
	if (role == Qt::EditRole) {
		TaskTreeItem* item = static_cast<TaskTreeItem*>(index.internalPointer());
		PC::Task* t = item->task();
		switch (column(index.column())) {
		case PC::Task::DATA_NAME:
			t->setName(value.toString());
			return true;
		case PC::Task::DATA_LENGTH: {
				PC::AtomTask* at = dynamic_cast<PC::AtomTask*>(item->task());
				if (at) {
					at->setLength(value.toInt());
				}
			}
			return true;
		default:
			break;
		}
	}
	return false;
}


QVariant
TaskTreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
	if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
		PC::Task::Data td = column(section);
		return m_app->taskTreeHeaderColName(td);
	}
	return QVariant();
}

QModelIndex
TaskTreeModel::index(int row, int column,
		const QModelIndex &parent) const {
	if (!hasIndex(row, column, parent)) {
		return QModelIndex();
	}
	TaskTreeItem *parentItem;
	if (!parent.isValid()) {
		parentItem = const_cast<TaskTreeItem*>(m_root);
	}
	else {
		parentItem = static_cast<TaskTreeItem*>(parent.internalPointer());
	}
	TaskTreeItem *child = parentItem->child(row);
	if (child) {
		return createIndex(row, column, child);
	}
	return QModelIndex();
}


QModelIndex
TaskTreeModel::parent(const QModelIndex &index) const {
	if (!index.isValid()) {
		return QModelIndex();
	}
	TaskTreeItem* item = static_cast<TaskTreeItem*>(index.internalPointer());
	TaskTreeItem* parentItem = item->parent();
	if(parentItem == m_root) {
		return QModelIndex();
	}
	return createIndex(parentItem->row(), 0, parentItem);
}


int
TaskTreeModel::rowCount(const QModelIndex &parent) const {
	TaskTreeItem *parentItem;
	if (parent.column() > 0)
		return 0;

	if (!parent.isValid())
		parentItem = const_cast<TaskTreeItem*>(m_root);
	else
		parentItem = static_cast<TaskTreeItem*>(parent.internalPointer());

	return parentItem->childCount();

}


bool TaskTreeModel::hasChildren(const QModelIndex &parent) const {
	TaskTreeItem *parentItem;
	if (!parent.isValid())
		parentItem = const_cast<TaskTreeItem*>(m_root);
	else
		parentItem = static_cast<TaskTreeItem*>(parent.internalPointer());

	return parentItem->childCount() > 0;
}


int TaskTreeModel::columnCount(const QModelIndex &/*parent*/) const {
	return m_columns.size()+1;
}

PC::Task::Data TaskTreeModel::column (int section) const {
	switch (section) {
	case 0:
		return m_columns.value(0);
		break;
	case 1:
		return PC::Task::DATA_NUM;
		break;
	default:
		return m_columns.value(section-1);
		break;
	}
}

void TaskTreeModel::setColumns(QList<PC::Task::Data> cols) {
	beginResetModel();
	m_columns = cols;
	m_columns.removeAll(PC::Task::DATA_NUM);
	endResetModel();
	emit columnsChanged();
}


TaskTreeItem*
TaskTreeModel::resolvItem(QString tbsId) {
	int point = tbsId.indexOf('.');
	if (point == -1) {
		if (tbsId == m_root->task()->tbsId()) {
			return m_root;
		}
		return 0;
	}
	QString befPoint = tbsId.left(point);
	QString aftPoint = tbsId.right(tbsId.length() - (point+1));
	if (befPoint == m_root->task()->tbsId()) {
		return m_root->resolvItem(aftPoint);
	}
	return 0;
}

QModelIndex
TaskTreeModel::resolvIndex(QString tbsId) {
	int last_p = tbsId.lastIndexOf('.');
	if (last_p == -1) {
		return QModelIndex();
	}
	QString aft = tbsId.right(tbsId.length()-last_p-1);
	QString bef = tbsId.left(last_p);
	return index(aft.toInt(), 0, resolvIndex(bef));
}


QModelIndex TaskTreeModel::indexForTask(const PC::Task *task) {
	return (task ? resolvIndex(task->tbsId()) : QModelIndex());
}


PC::Task* TaskTreeModel::taskForIndex(const QModelIndex &index) {
	if(!index.isValid()) {
		return m_root->task();
	}
	else {
		return static_cast<TaskTreeItem*>(index.internalPointer())->task();
	}
}


void TaskTreeModel::processTaskValueChanged(PC::Task *task, PC::Task::Data /*data*/) {
	QModelIndex p = indexForTask(task->parent());
	QModelIndex first = index(task->pos(), 0, p);
	QModelIndex last = index(task->pos(), columnCount(), p);
	this->dataChanged(first, last);
}

void TaskTreeModel::processTaskTimeChanged(PC::Task *task) {
	int fCol = -1, lCol = -1;
	QList<int> cols;

	cols.append(m_columns.indexOf(PC::Task::DATA_LENGTH));
	cols.append(m_columns.indexOf(PC::Task::DATA_START));
	cols.append(m_columns.indexOf(PC::Task::DATA_FINISH));
	cols.append(m_columns.indexOf(PC::Task::DATA_LATESTART));
	cols.append(m_columns.indexOf(PC::Task::DATA_LATEFINISH));
	cols.removeAll(-1);

	foreach (int c, cols) {
		if (c > lCol) {
			lCol = c;
		}
	}
	fCol = lCol;
	foreach (int c, cols) {
		if (c < fCol) {
			fCol = c;
		}
	}
	QModelIndex parent = indexForTask(task->parent());
	QModelIndex first = index(task->pos(), fCol, parent);
	QModelIndex last = index(task->pos(), lCol, parent);

	emit dataChanged(first, last);
}


void TaskTreeModel::processTaskCreated(PC::MacroTask *parent, PC::Task *task) {
	TaskTreeItem* parentItem = resolvItem(parent->tbsId());
	TaskTreeItem* newItem = new TaskTreeItem(task, parentItem);
	int childPos = parent->posForTask(task);

	QModelIndex parentIndex = indexForTask(parent);

	beginInsertRows(parentIndex, childPos, childPos);
	parentItem->insertChild(newItem, childPos);
	endInsertRows();
}


void TaskTreeModel::processTaskWillDelete(PC::Task *task) {
	TaskTreeItem* parentItem = resolvItem(task->parent()->tbsId());

	QModelIndex parentIndex = indexForTask(task->parent());

	int pos = task->pos();

	beginRemoveRows(parentIndex, pos, pos);
	parentItem->removeChild(pos);
	endRemoveRows();
}


void TaskTreeModel::processTaskTransformed(PC::MacroTask *parent, int pos) {
	TaskTreeItem* parentItem = resolvItem(parent->tbsId());
	PC::Task* t = parent->task(pos);

	parentItem->child(pos)->setTask(t);
}


void TaskTreeModel::processTaskMoved(PC::MacroTask *parentTo, PC::Task *parentFrom,
									int posTo, int posFrom) {
	QModelIndex parentFromIndex = indexForTask(parentFrom);
	QModelIndex parentToIndex = indexForTask(parentTo);

	beginMoveRows(parentFromIndex, posFrom, posFrom, parentToIndex, posTo);

	TaskTreeItem* parentFromItem = resolvItem(parentFrom->tbsId());
	TaskTreeItem* parentToItem = resolvItem(parentTo->tbsId());
	TaskTreeItem* it = parentFromItem->child(posFrom);
	parentFromItem->removeChild(posFrom);
	parentToItem->insertChild(it, posTo);

	endMoveRows();
}

